package com.googlecode.nls4j;

import java.util.Locale;
import java.util.Set;

/**
 * This BundleStorage contains a set of {@linkplain KeyValuePair} keys which is
 * updated (calling the {@linkplain KeyValuePair#setValue(Object)} method on
 * each key) each time when the method {@link #getBundle(Locale)} is called with
 * a different locale.
 * 
 * @author Andrey Pavlenko
 */
public class KeyValuePairStorage<K extends KeyValuePair<V>, V> extends
        BundleStorage<K, V> {
    private final Set<K> keys;
    private final KeyValuePairBundle<K, V> bundle = new KeyValuePairBundle<K, V>();

    public KeyValuePairStorage(final Class<K> keyType,
            final BundleLoader<K, V> bundleLoader, final Set<K> keys)
            throws NullPointerException {
        super(keyType, bundleLoader);
        this.keys = keys;
    }

    /**
     * Returns the {@linkplain KeyValuePair}s this bundle contains.
     */
    public Set<K> getKeys() {
        return keys;
    }

    /**
     * This method always returns the same NlsBundle object, but updates the
     * keys (calls the {@linkplain KeyValuePair#setValue(Object)} method on each
     * key) with the values, loaded via the {@linkplain #getBundleLoader()
     * BundleLoader}. The returning bundle's {@linkplain NlsBundle#get(NlsKey)}
     * method always returns {@linkplain KeyValuePair#getValue() key.getValue()}
     * .<br/>
     * <b>Note:</b> the keys are updated only if the specified {@code locale} is
     * not equals to {@linkplain NlsBundle#getLocale()} of the previously
     * returned bundle.
     */
    @Override
    public NlsBundle<K, V> getBundle(final Locale locale) {
        final KeyValuePairBundle<K, V> kvpBundle = this.bundle;

        if (locale.equals(kvpBundle.locale)) {
            return kvpBundle;
        }

        final NlsBundle<K, V> b = super.getBundle(locale);

        synchronized (this) {
            if (locale.equals(kvpBundle.locale)) {
                return kvpBundle;
            }

            kvpBundle.b = b;
            kvpBundle.locale = locale;

            for (final K key : getKeys()) {
                key.setValue(b.get(key));
            }

            return kvpBundle;
        }
    }

    private static final class KeyValuePairBundle<K extends KeyValuePair<V>, V>
            implements NlsBundle<K, V> {
        private volatile Locale locale;
        private volatile NlsBundle<K, V> b;

        @Override
        public V get(final K key) {
            return key.getValue();
        }

        @Override
        public boolean contains(final K key) {
            return b.contains(key);
        }

        @Override
        public Locale getLocale() {
            return locale;
        }
    }
}
